home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3.2
/
Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO
/
packet
/
n17jsrc
/
tcpuser.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-03
|
9KB
|
380 lines
/* User calls to TCP
* Copyright 1991 Phil Karn, KA9Q
*/
#include <stdio.h>
#include "global.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "socket.h"
#include "internet.h"
#include "tcp.h"
#include "ip.h"
#include "icmp.h"
#include "proc.h"
int16 Tcp_window = DEF_WND;
struct tcb *
open_tcp(lsocket,fsocket,mode,window,r_upcall,t_upcall,s_upcall,tos,user)
struct socket *lsocket; /* Local socket */
struct socket *fsocket; /* Remote socket */
int mode; /* Active/passive/server */
int16 window; /* Receive window (and send buffer) sizes */
void (*r_upcall)(); /* Function to call when data arrives */
void (*t_upcall)(); /* Function to call when ok to send more data */
void (*s_upcall)(); /* Function to call when connection state changes */
int tos;
int user; /* User linkage area */
{
struct connection conn;
register struct tcb *tcb;
if(lsocket == NULLSOCK){
Net_error = INVALID;
return NULLTCB;
}
conn.local.address = lsocket->address;
conn.local.port = lsocket->port;
if(fsocket != NULLSOCK){
conn.remote.address = fsocket->address;
conn.remote.port = fsocket->port;
} else {
conn.remote.address = 0;
conn.remote.port = 0;
}
if((tcb = lookup_tcb(&conn)) == NULLTCB){
if((tcb = create_tcb(&conn)) == NULLTCB){
Net_error = NO_MEM;
return NULLTCB;
}
} else if(tcb->state != TCP_LISTEN){
Net_error = CON_EXISTS;
return NULLTCB;
}
tcb->user = user;
if(window != 0)
tcb->window = tcb->rcv.wnd = window;
else
tcb->window = tcb->rcv.wnd = Tcp_window;
tcb->snd.wnd = 1; /* Allow space for sending a SYN */
tcb->r_upcall = r_upcall;
tcb->t_upcall = t_upcall;
tcb->s_upcall = s_upcall;
tcb->tos = tos;
switch(mode){
case TCP_SERVER:
tcb->flags.clone = 1;
case TCP_PASSIVE: /* Note fall-thru */
setstate(tcb,TCP_LISTEN);
break;
case TCP_ACTIVE:
/* Send SYN, go into TCP_SYN_SENT state */
tcb->flags.active = 1;
send_syn(tcb);
setstate(tcb,TCP_SYN_SENT);
tcp_output(tcb);
break;
}
return tcb;
}
/* User send routine */
int
send_tcp(tcb,bp)
register struct tcb *tcb;
struct mbuf *bp;
{
int16 cnt;
if(tcb == NULLTCB || bp == NULLBUF){
free_p(bp);
Net_error = INVALID;
return -1;
}
cnt = len_p(bp);
switch(tcb->state){
case TCP_CLOSED:
free_p(bp);
Net_error = NO_CONN;
return -1;
case TCP_LISTEN:
if(tcb->conn.remote.address == 0 && tcb->conn.remote.port == 0){
/* Save data for later */
append(&tcb->sndq,bp);
tcb->sndcnt += cnt;
break;
}
/* Change state from passive to active */
tcb->flags.active = 1;
send_syn(tcb);
setstate(tcb,TCP_SYN_SENT); /* Note fall-thru */
case TCP_SYN_SENT:
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT:
append(&tcb->sndq,bp);
tcb->sndcnt += cnt;
tcp_output(tcb);
break;
case TCP_FINWAIT1:
case TCP_FINWAIT2:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
free_p(bp);
Net_error = CON_CLOS;
return -1;
}
return (int)cnt;
}
/* User receive routine */
int
recv_tcp(tcb,bpp,cnt)
register struct tcb *tcb;
struct mbuf **bpp;
int16 cnt;
{
if(tcb == NULLTCB || bpp == (struct mbuf **)NULL){
Net_error = INVALID;
return -1;
}
if(tcb->rcvcnt == 0){
/* If there's nothing on the queue, our action depends on what state
* we're in (i.e., whether or not we're expecting any more data).
* If no more data is expected, then simply return 0; this is
* interpreted as "end of file". Otherwise return -1.
*/
switch(tcb->state){
case TCP_LISTEN:
case TCP_SYN_SENT:
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
case TCP_FINWAIT1:
case TCP_FINWAIT2:
Net_error = WOULDBLK;
return -1;
case TCP_CLOSED:
case TCP_CLOSE_WAIT:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
*bpp = NULLBUF;
return 0;
}
}
/* cnt == 0 means "I want it all" */
if(cnt == 0)
cnt = tcb->rcvcnt;
/* See if the user can take all of it */
if(tcb->rcvcnt <= cnt){
cnt = tcb->rcvcnt;
*bpp = tcb->rcvq;
tcb->rcvq = NULLBUF;
} else {
*bpp = ambufw(cnt);
pullup(&tcb->rcvq,(*bpp)->data,cnt);
(*bpp)->cnt = cnt;
}
tcb->rcvcnt -= cnt;
tcb->rcv.wnd += cnt;
/* Do a window update if it was closed */
if(cnt == tcb->rcv.wnd){
tcb->flags.force = 1;
tcp_output(tcb);
}
return (int)cnt;
}
/* This really means "I have no more data to send". It only closes the
* connection in one direction, and we can continue to receive data
* indefinitely.
*/
int
close_tcp(tcb)
register struct tcb *tcb;
{
if(tcb == NULLTCB){
Net_error = INVALID;
return -1;
}
switch(tcb->state){
case TCP_CLOSED:
return 0; /* Unlikely */
case TCP_LISTEN:
case TCP_SYN_SENT:
close_self(tcb,NORMAL);
return 0;
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate(tcb,TCP_FINWAIT1);
tcp_output(tcb);
return 0;
case TCP_CLOSE_WAIT:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate(tcb,TCP_LAST_ACK);
tcp_output(tcb);
return 0;
case TCP_FINWAIT1:
case TCP_FINWAIT2:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
Net_error = CON_CLOS;
return -1;
}
return -1; /* "Can't happen" */
}
/* Delete TCB, free resources. The user is not notified, even if the TCB is
* not in the TCP_CLOSED state. This function should normally be called by the
* user only in response to a state change upcall to TCP_CLOSED state.
*/
int
del_tcp(conn)
struct tcb *conn;
{
register struct tcb *tcb;
struct tcb *tcblast = NULLTCB;
struct reseq *rp,*rp1;
/* Remove from list */
for(tcb=Tcbs;tcb != NULLTCB;tcblast = tcb,tcb = tcb->next)
if(tcb == conn)
break;
if(tcb == NULLTCB){
Net_error = INVALID;
return -1; /* conn was NULL, or not on list */
}
if(tcblast != NULLTCB)
tcblast->next = tcb->next;
else
Tcbs = tcb->next; /* was first on list */
stop_timer(&tcb->timer);
for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
rp1 = rp->next;
free_p(rp->bp);
free((char *)rp);
}
tcb->reseq = NULLRESEQ;
free_p(tcb->rcvq);
free_p(tcb->sndq);
free((char *)tcb);
return 0;
}
/* Return 1 if arg is a valid TCB, 0 otherwise */
int
tcpval(tcb)
struct tcb *tcb;
{
register struct tcb *tcb1;
if(tcb == NULLTCB)
return 0; /* Null pointer can't be valid */
for(tcb1=Tcbs;tcb1 != NULLTCB;tcb1 = tcb1->next){
if(tcb1 == tcb)
return 1;
}
return 0;
}
/* Kick a particular TCP connection */
int
kick_tcp(tcb)
register struct tcb *tcb;
{
if(!tcpval(tcb))
return -1;
tcb->flags.force = 1; /* Send ACK even if no data */
tcp_timeout(tcb);
return 0;
}
/* Kick all TCP connections to specified address; return number kicked */
int
kick(addr)
int32 addr;
{
register struct tcb *tcb;
int cnt = 0;
for(tcb=Tcbs;tcb != NULLTCB;tcb = tcb->next){
if(tcb->conn.remote.address == addr){
kick_tcp(tcb);
cnt++;
}
}
return cnt;
}
/* Clear all TCP connections */
void
reset_all()
{
register struct tcb *tcb;
for(tcb=Tcbs;tcb != NULLTCB;tcb = tcb->next)
reset_tcp(tcb);
pwait(NULL); /* Let the RSTs go forth */
}
void
reset_tcp(tcb)
register struct tcb *tcb;
{
struct tcp fakeseg;
struct ip fakeip;
if(tcb == NULLTCB)
return;
if(tcb->state != TCP_LISTEN){
/* Compose a fake segment with just enough info to generate the
* correct RST reply
*/
memset((char *)&fakeseg,0,sizeof(fakeseg));
memset((char *)&fakeip,0,sizeof(fakeip));
fakeseg.dest = tcb->conn.local.port;
fakeseg.source = tcb->conn.remote.port;
fakeseg.flags.ack = 1;
/* Here we try to pick a sequence number with the greatest likelihood
* of being in his receive window.
*/
fakeseg.ack = tcb->snd.nxt + tcb->snd.wnd - 1;
fakeip.dest = tcb->conn.local.address;
fakeip.source = tcb->conn.remote.address;
fakeip.tos = tcb->tos;
reset(&fakeip,&fakeseg);
}
close_self(tcb,RESET);
}
#ifdef notused
/* Return character string corresponding to a TCP well-known port, or
* the decimal number if unknown.
*/
char *
tcp_port(n)
int16 n;
{
static char buf[32];
switch(n){
case IPPORT_ECHO:
return "echo";
case IPPORT_DISCARD:
return "discard";
case IPPORT_FTPD:
return "ftp_data";
case IPPORT_FTP:
return "ftp";
case IPPORT_TELNET:
return "telnet";
case IPPORT_SMTP:
return "smtp";
case IPPORT_POP:
return "pop";
default:
sprintf(buf,"%u",n);
return buf;
}
}
#endif